﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using CoreEx.Common.Extensions;
using CoreEx.DynamicInvoker;
using LinFu.AOP.Interfaces;
using LinFu.Proxy.Interfaces;

namespace CoreEx.Database.Persistence
{
    public class ObjectInvokeWrapper : IInvokeWrapper
    {
        private object _target;
        private IChangeTracker _changeTracker;
        private IDictionary<object, object> _proxyItems = new Dictionary<object, object>();


        public ObjectInvokeWrapper(object target, IChangeTracker changeTracker)
        {
            _target = target;
            _changeTracker = changeTracker;
        }

        public void BeforeInvoke(IInvocationInfo info)
        {
            //throw new NotImplementedException();
        }

        public void AfterInvoke(IInvocationInfo info, object returnValue)
        {
            //throw new NotImplementedException();
        }

        public object DoInvoke(IInvocationInfo info)
        {            
            var returnValue = info.TargetMethod.DynamicInvoke(_target,info.Arguments);                                     
            var proxy = GetProxy(returnValue,info.TargetMethod.ReturnType);
            return proxy;            
        }

        private object GetProxy(object target, Type targetType)
        {
            if (!IsList(targetType) && !IsComplexType(targetType))
                return target;
            
            if (!_proxyItems.ContainsKey(target))
                _proxyItems.Add(target, CreateProxy(target,targetType));
            return _proxyItems[target];
        }

        private object CreateProxy(object target, Type targetType)
        {            
            if (IsComplexType(targetType))
                return _changeTracker.ProxyFactory.CreateProxy(targetType,
                                                               new ObjectInvokeWrapper(target, _changeTracker),typeof(INotifyPropertyChanged));
            if (IsList(targetType))
            {
                var elementType = targetType.GetGenericElementType();
                var enumeratorType = typeof (IEnumerator<>).MakeGenericType(elementType);

                return _changeTracker.ProxyFactory.CreateProxy(targetType, new ListInvokeWrapper(target, _changeTracker),
                                                               enumeratorType);                
            }

            return target;

        }

        private bool IsList(Type type)
        {
            return typeof(IList).IsAssignableFrom(type);
        }


        private bool IsComplexType(Type type)
        {
            return (!type.IsSystemType() && type.IsEnumerable());
        }
    }
}
